﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Pex.Engine.Packages;
using Microsoft.Pex.Framework.ComponentModel;
using Microsoft.Pex.Engine.ComponentModel;
using Microsoft.ExtendedReflection.Metadata.Names;
using Microsoft.Pex.Engine.Explorable;
using Microsoft.ExtendedReflection.Metadata;
using Microsoft.ExtendedReflection.ComponentModel;
using Microsoft.ExtendedReflection.Feedback;

namespace HintProvider.Factory
{
    public sealed class LocalExplorableRecommender
        : PexExplorationComponentBase, IService
    {
        AssemblyEx currAssembly;
        protected override void Initialize()
        {
            base.Initialize();            
            this.currAssembly = this.Services.CurrentAssembly.Assembly.Assembly;
            this.Log.LogMessage("hint provider", "Hint provider initialized");
        }

        #region IPexTypeHintProvider Members

        public PexExplorableCandidate GetExplorableCandidate()
        {
            TypeEx intstacktdef = null;
            foreach (var tdef in this.currAssembly.TypeDefinitions)
            {
                if (tdef.FullName.Contains("IntStack"))
                {
                    intstacktdef = tdef.Instantiate(new TypeEx[0] { });
                }
            }

            foreach (var tdef in this.currAssembly.TypeDefinitions)
            {
                if (tdef.FullName.Contains("IntStack_factory"))
                {
                    this.Log.LogMessage("hint provider", "Found the factory");

                    foreach (var mdef in tdef.DeclaredStaticMethods)
                    {
                        PexExplorableFactory originalExplorableFactory;
                        var result = PexExplorableFactory.TryGetExplorableFactory(this, intstacktdef, out originalExplorableFactory);

                        Method m = mdef.Instantiate(new TypeEx[0], new TypeEx[0]); 

                        //add constructor
                        if (!originalExplorableFactory.TrySetFactoryMethod(m))
                        {                            
                        }

                        IPexExplorable originalExplorable1 = originalExplorableFactory.CreateExplorable();
                        CodeUpdate.AddMethodCodeUpdate originalPreviewUpdate1;
                        CodeUpdate originalUpdate1 = originalExplorableFactory.CreateExplorableFactoryUpdate(out originalPreviewUpdate1);

                        this.Log.LogMessage("hint provider", "Returning the factory");

                        //return the original one after own suggested            
                        return new PexExplorableCandidate(originalExplorable1, false, originalUpdate1);
                    }
                }
            }

            return null;
        }      
        #endregion
    }


    public class PexMeExplorableRecommenderAttribute
        : PexComponentElementDecoratorAttributeBase, IPexExplorationPackage
    {
        LocalExplorableRecommender ler;

        #region IPexExplorationPackage Members

        protected sealed override void Decorate(Name location, IPexDecoratedComponentElement host)
        {
            host.AddExplorationPackage(location, this);
        }

        public void AfterExploration(IPexExplorationComponent host, object data)
        {
            host.Log.LogMessage("factory guesser", "AfterExploration");
        }

        public object BeforeExploration(IPexExplorationComponent host)
        {
            host.Log.LogMessage("factory guesser", "BeforeExploration");
            host.ExplorationServices.ExplorableManager.AddExplorableCandidate(ler.GetExplorableCandidate());
            return null;
        }

        public void Initialize(IPexExplorationEngine host)
        {
            host.Log.LogMessage("factory guesser", "Initialize");
            this.ler = host.GetService<LocalExplorableRecommender>();            
        }             

        public void Load(Microsoft.ExtendedReflection.ComponentModel.IContainer explorationContainer)
        {
            explorationContainer.AddComponent("LocalExplorable", new LocalExplorableRecommender());
        }
        #endregion

        #region IPexExplorationPackage Members
        public string Name
        {
            get { return "PexMeExplorableRecommender"; }
        }       

        #endregion
    }
}
